package com.atguigu.gmall.search.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PrimitiveArraySerializer;
import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.atguigu.gmall.search.pojo.Goods;
import com.atguigu.gmall.search.pojo.SearchParamVo;
import com.atguigu.gmall.search.pojo.SearchResponseAttrVo;
import com.atguigu.gmall.search.pojo.SearchResponseVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class SearchService {

    @Autowired
    RestHighLevelClient restHighLevelClient;

    public SearchResponseVo search(SearchParamVo searchParamVo) {

        //构建dsl语句
        SearchRequest searchRequest = new SearchRequest(new String[]{"goods"}, buildDsl(searchParamVo));
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //解析查询参数
            SearchResponseVo searchResponseVo = paseResult(response);
            searchResponseVo.setPageNum(searchParamVo.getPageNum());
            searchResponseVo.setPageSize(searchParamVo.getPageSize());
            return searchResponseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    private SearchResponseVo paseResult(SearchResponse response) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        SearchHits hits = response.getHits();
        searchResponseVo.setTotal(hits.getTotalHits().value);
        SearchHit[] searchHits = hits.getHits();
        //获取搜索数据
        searchResponseVo.setGoodsList(Arrays.stream(searchHits).map(item->{
            String sourceAsString = item.getSourceAsString();
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);
            //获取高亮数据
            Map<String, HighlightField> highlightFields = item.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            goods.setTitle(title.fragments()[0].string());
            return goods;
        }).collect(Collectors.toList()));
        //获取品牌聚合
        Aggregations aggregations = response.getAggregations();
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> buckets = brandIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(buckets)){
            searchResponseVo.setBrands(buckets.stream().map(item->{
                BrandEntity brandEntity = new BrandEntity();
                brandEntity.setId(item.getKeyAsNumber().longValue());
                //获取所有子聚合
                Aggregations aggregations1 = item.getAggregations();
                ParsedStringTerms brandName = aggregations1.get("brandNameAgg");
                //获取品牌名称子聚合的桶
                List<? extends Terms.Bucket> nameBuckets = brandName.getBuckets();
                if (!CollectionUtils.isEmpty(nameBuckets)){
                    brandEntity.setName(nameBuckets.get(0).getKeyAsString());
                }
                //获取品牌的logo聚合
                ParsedStringTerms logoAgg = aggregations1.get("logoAgg");
                List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                if (!CollectionUtils.isEmpty(logoAggBuckets)){
                    brandEntity.setLogo(logoAggBuckets.get(0).getKeyAsString());
                }
                return brandEntity;
            }).collect(Collectors.toList()));
        }
        //获取分类聚合
        ParsedLongTerms categoryAgg = aggregations.get("categoryAgg");
        List<? extends Terms.Bucket> categoryAggBuckets = categoryAgg.getBuckets();
        if (!CollectionUtils.isEmpty(categoryAggBuckets)){
            searchResponseVo.setCategories(categoryAggBuckets.stream().map(item->{
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(item.getKeyAsNumber().longValue());
                Aggregations itemAggregations = item.getAggregations();
                ParsedStringTerms categoryNameAgg = itemAggregations.get("categoryNameAgg");
                List<? extends Terms.Bucket> categoryNameAggBuckets = categoryNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(categoryNameAggBuckets)){
                    categoryEntity.setName(categoryNameAggBuckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }
        //获取规格参数聚合
        // 规格参数的聚合结果集
        ParsedNested attrAgg = aggregations.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> attrAggBuckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(attrAggBuckets)){
            searchResponseVo.setFilters(attrAggBuckets.stream().map(item->{
                SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
                searchResponseAttrVo.setAttrId(item.getKeyAsNumber().longValue());
                //获取子聚合
                ParsedStringTerms attrNameAgg = item.getAggregations().get("attrNameAgg");
                List<? extends Terms.Bucket> nameAggBuckets = attrNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(nameAggBuckets)){
                    searchResponseAttrVo.setAttrName(nameAggBuckets.get(0).getKeyAsString());
                }
                ParsedStringTerms attrValueAgg = item.getAggregations().get("attrValueAgg");
                List<? extends Terms.Bucket> valueAggBuckets = attrValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(valueAggBuckets)){
                    searchResponseAttrVo.setAttrValues(valueAggBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                }
                return searchResponseAttrVo;
            }).collect(Collectors.toList()));
        }

        return searchResponseVo;
    }

    private SearchSourceBuilder buildDsl(SearchParamVo searchParamVo) {
        //判断keyword是否为空
        String keyword = searchParamVo.getKeyword();
        if (StringUtils.isBlank(keyword)){
            throw new RuntimeException("搜索关键字不能为空");
        }
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1构建匹配查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        //1.2构建过滤条件
        //1.2.1构建品牌过滤
        List<Long> brandId = searchParamVo.getBrandId();
        if (!CollectionUtils.isEmpty(brandId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",brandId));
        }
        //1.2.2构建分类过滤
        List<Long> categoryId = searchParamVo.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId",categoryId));
        }
        //1.2.3构建价格区间
        Double priceFrom = searchParamVo.getPriceFrom();
        Double priceTo = searchParamVo.getPriceTo();
        if (priceFrom!=null||priceTo!=null){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            if (priceFrom!=null){
                boolQueryBuilder.filter(rangeQuery.gte(priceFrom));
            }
            if (priceTo!=null){
                boolQueryBuilder.filter(rangeQuery.lte(priceTo));
            }
        }
        //1.2.4构建是否有货
        Boolean store = searchParamVo.getStore();
        if (store!=null){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("store",store));
        }
        //构建过滤规格参数
        List<String> props = searchParamVo.getProps();
        if (!CollectionUtils.isEmpty(props)){
            props.forEach(item->{
                String[] split = StringUtils.split(item,":");
                if (split!=null&&split.length==2){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None));
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId",split[0]));
                    //规格参数8-12g 在分割
                    String[] attr = StringUtils.split(split[1], "-");
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue",attr));
                }
            });
        }
        //构建排序 排序条件：0-得分排序 1-价格降序 2-价格升序 3-销量降序 4-新品降序
        Integer sort = searchParamVo.getSort();
        if (sort!=null){
            switch (sort){
                case 1:
                    searchSourceBuilder.sort("price", SortOrder.DESC);
                    break;
                case 2:
                    searchSourceBuilder.sort("price",SortOrder.ASC);
                    break;
                case 3:
                    searchSourceBuilder.sort("sales",SortOrder.DESC);
                    break;
                case 4:
                    searchSourceBuilder.sort("createTime",SortOrder.DESC);
                    break;
                default: searchSourceBuilder.sort("_scope",SortOrder.DESC);break;
            }
        }
        //构建分页查询
        Integer pageNum = searchParamVo.getPageNum();
        Integer pageSize = searchParamVo.getPageSize();
        searchSourceBuilder.from((pageNum-1)*pageSize);
        searchSourceBuilder.size(pageSize);
        //构建高亮查询
        searchSourceBuilder.highlighter(new HighlightBuilder()
               .field("title")
               .preTags("<font style='color:red'>")
                .postTags("</font>"));
        //构建聚合查询
        //聚合品牌
        searchSourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        //构建分类聚合
        searchSourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        //构建规格参数聚合
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));
        searchSourceBuilder.fetchSource(new String[]{"skuId", "title", "subtitle", "price", "defaultImage"}, null);
        searchSourceBuilder.query(boolQueryBuilder);
        System.out.println(searchSourceBuilder);
        return searchSourceBuilder;
    }
}
